import uuid
import hashlib
from libs.utils import AttrDict
from xml.etree import ElementTree
from public.constants import logger


class PayUtil(object):
    @staticmethod
    def nonce_str():
        return uuid.uuid4().hex

    @staticmethod
    def get_signature(mch_key: str, params: dict) -> str:
        buffer = ['{0}={1}'.format(k, params[k]) for k in sorted(params) if params[k]]
        buffer.append('key={0}'.format(mch_key))
        sign_str = '&'.join(buffer).encode('utf-8')
        logger.info(f"签名字符串: {sign_str}")
        signature = hashlib.md5(sign_str).hexdigest().upper()
        logger.info(f"生成签名: {signature}")
        return signature

    @staticmethod
    def add_signature(mch_key: str, key: str, params: dict) -> None:
        logger.info(f"生成签名，参数: {params}, 密钥: {mch_key}")
        params[key] = PayUtil.get_signature(mch_key, params)

    @staticmethod
    def dict_to_xml(params: dict) -> str:
        buffer = ['<xml>']
        for k, v in params.items():
            buffer.append('<{0}><![CDATA[{1}]]></{0}>'.format(k, v))
        buffer.append('</xml>')
        return ''.join(buffer)

    @staticmethod
    def xml_to_dict(data: str) -> AttrDict:
        buffer = AttrDict()
        root = ElementTree.fromstring(data)
        for child in root:
            buffer[child.tag] = child.text
        return buffer

    @staticmethod
    def parse_pay_response(data: str, mch_key: str, abort: bool = True) -> (AttrDict, bool, str):
        response = PayUtil.xml_to_dict(data)
        exception_msg = None
        if response.return_code != 'SUCCESS':
            exception_msg = '微信支付异常: return_msg: {0}.'.format(response.return_msg)
        elif response.pop('sign') != PayUtil.get_signature(mch_key, response):
            exception_msg = '微信支付异常: 解析返回结果签名校验失败'
        elif response.result_code != 'SUCCESS':
            exception_msg = '微信支付异常: err_code: {0.err_code}, err_code_des: {0.err_code_des}'.format(response)
        if exception_msg:
            if abort:
                raise Exception(exception_msg)
            else:
                logger.error(exception_msg)
        return response, exception_msg is None, exception_msg
